﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;

namespace oled
{
    /// 取模方向 与 扫描模式共同作用
    class OLED_Align_Mode
    {
        public const bool Big_endian = false;    //大端对齐模式 顺向
        public const bool Little_endian = true;  //小端对齐模式 逆向
    }
    public enum OLED_Scan_Mode
    {
        //Vertical_scan = 0,      //垂直扫描(字节垂直,垂直递增)
        //Horizontal_scan = 1,    //水平扫描(字节水平,水平递增)
        VertiByte_HoriScan = 2, //列行式(字节垂直,水平递增)
        HoriByte_Vertiscan = 3, //行列式(字节水平,垂直递增)
    }
    public class OLED_BYTE
    {
        public int X;
        public int Y;
        public byte bytes;
        public bool[] points; 
        public OLED_BYTE(int x, int y)
        {
            X = x;
            Y = y;
        }
        public OLED_BYTE(int x, int y, byte data)
        {
            X = x;
            Y = y;
            bytes = data;
        }
        public OLED_BYTE(int x, int y, byte data, bool bit_order)
        {
            X = x;
            Y = y;
            bytes = data;
            points = Bit_order.BoolArray(bytes, bit_order);
        }
    }
    public partial class OLED_Drive
    {
        /// <summary>
        /// 屏幕的参数
        /// </summary>
        private static int OLED_DPI = 3;
        private static int OLED_Height = 64;
        private static int OLED_Width = 128;
        private static readonly bool Bit_Order = OLED_Align_Mode.Little_endian;
        private static readonly bool PixelSytle = false;
        private static readonly OLED_Scan_Mode Scan_Mode = OLED_Scan_Mode.VertiByte_HoriScan;

        public static bool[,] oled_mem;

        #region oled扫描模式，模拟硬件底层逻辑，模式不一致会导致显示错误，仅实现列行式待完善
        /// <summary>
        /// 生成一个像素点
        /// </summary>
        /// <param name="x">横坐标</param>
        /// <param name="y">纵坐标</param>
        /// <returns></returns>
        private Rectangle PixelPoint(int x, int y)
        {
            int PixelSize = OLED_DPI;

            if (PixelSytle == true)
            {
                PixelSize--;
            }
            x *= PixelSize;
            y *= PixelSize;

            return new Rectangle(new Point(x, y), new Size(PixelSize, PixelSize));
        }

        /// <summary>
        /// 字节数据转点图
        /// </summary>
        /// <param name="oled">包含位置信息的一个字节数据</param>
        /// <returns></returns>
        private List<Rectangle> VH_GetPoints(OLED_BYTE oled)
        {
            List<Rectangle> PointList = new List<Rectangle>();

            if (oled.X >= OLED_Width || oled.Y >= OLED_Height) return PointList;
            PointList.Clear();

            for (int y = 0; y < 8; y++)
            {
                if (oled.points[y])
                {
                    Rectangle point = PixelPoint(oled.X, y + oled.Y);
                    PointList.Add(point);
                }
            }
            return PointList;
        }
        private void HV_Mode(int x, int y, byte data)
        {
            if (x >= OLED_Width || y >= OLED_Height) return;

            bool[] points = Bit_order.BoolArray(data, Bit_Order);

            if (points.Length <= 0) return;

            for (int offset = 0; offset < 8; offset++)
            {
                oled_mem[y, x + offset] = points[offset];
            }
        }
        private void VH_Mode(int x, int y, byte data)
        {
            if (x >= OLED_Width || y >= OLED_Height) return;

            bool[] points = Bit_order.BoolArray(data, Bit_Order);

            if (points.Length <= 0) return;

            for (int offset = 0; offset < 8; offset++)
            {
                //映射此点信息到buffer
                oled_mem[y + offset, x] = points[offset];
            }
        }
        private void drawOneByte(int x, int y, byte data)// 绘制一个字节数据
        {
            switch (Scan_Mode)
            {
                //case Oled_Mode.Vertical_scan:
                //    break;
                //case Oled_Mode.Horizontal_scan:
                //    break;
                case OLED_Scan_Mode.VertiByte_HoriScan:
                    {
                        VH_Mode(x, y, data);// 列行式
                        break;
                    }
                case OLED_Scan_Mode.HoriByte_Vertiscan:
                    {
                        HV_Mode(x, y, data);// 行列式
                        break;
                    }
                default:
                    break;
            }
        }
        private void DrawChar(int x, int y, byte asiic, int size)
        {
            int index = asiic - ' ';
            switch (size)
            {
                case 6:
                    {
                        index *= 6;//F6X8
                        for (int i = 0; i < 6; i++)
                        {
                            drawOneByte(x + i, y, Fonts.F6X8[index + i]);
                        }
                        break;
                    }
                case 8:
                    {
                        index *= 8;
                        for (int i = 0; i < 6; i++)
                        {
                            //drawOneByte(x + i, y, Fonts.F6X8[index + i]);
                        }
                        break;
                    }
            }
        }

        /// <summary>
        /// 字节数据更新到oled内存上, 列行式
        /// </summary>
        /// <param name="Pos_x">横坐标</param>
        /// <param name="Pos_y">纵坐标</param>
        /// <param name="data">字节数据</param>
        /// <param name="offset_x"></param>
        public void Refresh_Memory(int Pos_x, int Pos_y, Byte data, int offset_x)
        {
            bool[] point = Bit_order.BoolArray(data, Bit_Order);
            int addr_column = Pos_x + offset_x;
            for (int bitx = 0; bitx < 8; bitx++)
            {
                int addr_row = Pos_y + bitx;
                if (addr_column >= OLED_Width || Pos_y + bitx >= OLED_Height) break;
                oled_mem[addr_row, addr_column] = point[bitx];
            }
        }

        /// <summary>
        /// 屏幕大小的byte数组更新到oled上
        /// </summary>
        /// <param name="bmp">字节数组</param>
        /// <param name="oled">面板</param>
        public void Refresh_ByteMemory(byte[] bmp, Bitmap oled)
        {
            byte[] line = new byte[OLED_Width]; //1 page 128*8
            List<Rectangle>
                PointList = new List<Rectangle>();
            PointList.Clear();

            OLED_BYTE oled_points;

            int loop = OLED_Height / 8;

            if (bmp.Length != OLED_Height * OLED_Width / 8) return;

            if (OLED_Height % 8 != 0) loop++;

            for (int page = 1, y = 0; page <= loop; page++, y += 8)
            {
                Array.Copy(bmp, OLED_Width * (page - 1), line, 0, OLED_Width);

                for (int x = 0; x < OLED_Width; x++)
                {
                    oled_points = new OLED_BYTE(x, y, line[x], Bit_Order);
                    PointList.AddRange(VH_GetPoints(oled_points));
                }
            }

            Rectangle[] points = PointList.ToArray();

            if (points.Length != 0)
            {
                Graphics Graphic = Graphics.FromImage(oled);
                Brush brush = new SolidBrush(Color.FromArgb(255, 255, 255));
                Graphic.Clear(Color.Black);
                Graphic.FillRectangles(brush, points);
            }
        }
        #endregion

        #region 应用层接口，用于通信
        public void Clear()//全屏清空
        {
            oled_mem = new bool[OLED_Height, OLED_Width];
        }
        public void ShowChar(int x, int y, byte asiic, int size)//显示一个字符
        {
            DrawChar(x, y, asiic, size);
        }
        public void ShowString(int x, int y, string text, int size)//显示一个字符串
        {
            byte[] data = Encoding.Default.GetBytes(text);
            for (int loop = 0; loop < text.Length; loop++)
            {
                DrawChar(x, y, data[loop], size);
                x += (size);
            }
        }        
        public void ShowBMP(int Pos_x, int Pos_y, int width, int height, byte[] bmp)//显示bmp
        {
            //为什么要先映射到map上显示的时候又转成bmp呢
            //为了更真实的模拟oled中的 列行式 行列式扫描
            byte[] oneline = new byte[width]; //屏幕一行buff
            int loop = height / 8;

            if (Pos_x >= OLED_Width || Pos_y >= OLED_Height) return;

            if (height % 8 != 0) loop++;

            for (int page = 1, y = 0; page <= loop; page++, y += 8)
            {
                Array.Copy(bmp, width * (page - 1), oneline, 0, width);
                for (int x = 0; x < width; x++)
                {   //一行BMP的字节数据映射到指定位置的oled_map(点阵)上
                    Refresh_Memory(Pos_x, Pos_y + y, oneline[x], x);
                }
            }
        }
        public void Windows_Clear(int x, int y, int width, int height)//清除指定区域 待完善
        {
            for (int row = 0; row < height; row++)
            {
                for (int colum = 0; colum < width; colum++)
                {
                    oled_mem[row + x, colum + y] = false;
                }
            }
        }
        #endregion
        #region 应用层接口，更新显示
        public void Refresh(Bitmap oled)//更新面板
        {
            byte[] bmp = new byte[OLED_Height * OLED_Width / 8];
            int cnt = 0;
            for (int row = 0; row < OLED_Height; row += 8)
            {
                bool[] data = new bool[8];
                for (int x = 0; x < OLED_Width; x++)
                {
                    for (int offset = 0; offset < 8; offset++)
                    {
                        data[offset] = oled_mem[offset + row, x];
                    }
                    bmp[cnt] = Bit_order.ToByte(data, Bit_Order);
                    cnt++;
                }
            }
            Refresh_ByteMemory(bmp, oled);
        }
        #endregion

        #region  OLED屏幕参数设置

        /// <summary>
        /// 设置屏幕缓冲区大小
        /// </summary>
        /// <param name="Row">屏幕纵向分辨率 行数</param>
        /// <param name="Colum">屏幕横向分辨率 列数</param>
        private void Set_Screen_Buff(int Row, int Colum)
        {
            oled_mem = new bool[Row, Colum];
        }

        /// <summary>
        /// 设置像素点大小 适应屏幕
        /// </summary>
        /// <param name="Row"></param>
        /// <param name="Colum"></param>
        private  void Set_DPI(int Row, int Colum)
        {
            OLED_Height = Row;
            OLED_Width = Colum;

            OLED_DPI = 3;
            if (OLED_Height < 64 || OLED_Width < 128)
            {
                OLED_DPI++;
            }
        }

        /// <summary>
        /// 设置窗口大小 分辨率低时选择尽量调大窗口
        /// </summary>
        /// <param name="height">oled 面板的高度</param>
        /// <param name="width">oled 面板的宽度</param>
        public void Set_PaintPanel(ref int height, ref int width)
        {
            if (height % 8 != 0)
            {
                height = (height / 8 + 1) * 8;
            }
            if (width % 8 != 0)
            {
                width = (width / 8 + 1) * 8;
            }
            Set_DPI(height, width);
            Set_Screen_Buff(height, width);

            height *= OLED_DPI;
            width *= OLED_DPI;
        }
        #endregion

    }
}
